﻿//Copyright (C) 2009  Jaco (ScionBot.com)

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Scion.Library;
using System.Drawing;
using System.Reflection;
using Scion.Misc;
using Scion.Library.ReturnType;
using Scion.Library.Antiban;
using Scion.Library.RS;
namespace Scion.Core
{
    /// <summary>
    /// This class basically executes a selected script.
    /// </summary>
    public class Script
    {
        public Thread scriptThread;
        public string scriptName;
        public string scriptAuthor;
        public bool blockedInput = false;
        public bool scriptRunning = false;
        public bool scriptPaused = false;
        public bool scriptSkipCheckTabs = false;
        public Type scriptInstance;
        public object scriptObject;
        public PaintObject status;
        public Assembly scriptAssembly;
        Stub stub;
        FailSafe safeFail;

        public Script(Stub s, string nameOfScript)
        {
            stub = s;
            scriptRunning = false;
            scriptPaused = false;
            scriptName = nameOfScript;
            status = new PaintObject(scriptName, Brushes.Red, new Point(10, 10));
            safeFail = new FailSafe(stub);
        }

        public static string GetScriptAuthor(String scrName)
        {
            Assembly asm = Assembly.LoadFrom(ScionSettings.StartupPath + @"\Scripts\" + scrName + ".dll");
            foreach (Type scriptClass in asm.GetTypes())
            {
                if (scriptClass.IsClass && scriptClass.IsSubclassOf(typeof(Superior)))
                {
                    FieldInfo[] fields = scriptClass.GetFields();
                    foreach (FieldInfo field in fields)
                    {
                        if ((field.Name == "ScriptAuthor") && field.IsStatic)
                            return (string)field.GetValue(null);
                    }
                }
            }
            return "N/A";
        }

        public static string GetScriptComment(String scrName)
        {
            Assembly asm = Assembly.LoadFrom(ScionSettings.StartupPath + @"\Scripts\" + scrName + ".dll");
            foreach (Type scriptClass in asm.GetTypes())
            {
                if (scriptClass.IsClass && scriptClass.IsSubclassOf(typeof(Superior)))
                {
                    FieldInfo[] fields = scriptClass.GetFields();
                    foreach (FieldInfo field in fields)
                    {
                        if ((field.Name == "ScriptComment") && field.IsStatic)
                            return (string)field.GetValue(null);
                    }
                }
            }
            return "N/A";
        }

        /// <summary>
        /// Raises a flag for the Execution Loop to break and come to an end.
        /// </summary>
        public void Stop()
        {
            if (!scriptRunning)
                return;
            scriptRunning = false;
            scriptPaused = false;
        }

        private void EndStop()
        {
            stub.botGraphics.RemoveAllObjects(false);
            blockedInput = false;
            //stub.botBridge.SyncronizeBridge();
            WriteEvent("Script Stopped!");
        }

        private void WriteEvent(string str)
        {
            stub.botLog.WriteLine(str);
        }

        public void Start()
        {
            stub.botAccount.DisableAutoLogin = false;
            blockedInput = true;
            stub.botBridge.SyncronizeBridge();
            if (scriptPaused)
            {
                scriptPaused = false;
                status.brush = Brushes.Green;
                WriteEvent("Script Resumed!");
                return;
            }
            else if (scriptRunning)
                return;
            scriptPaused = false;
            scriptThread = new Thread(Loop);
            scriptThread.Start();
            scriptRunning = true;
            status.brush = Brushes.Green;
            if (!stub.botGraphics.FindObject(status))
                stub.botGraphics.AddObject(status);
            WriteEvent("Script Started!");
        }

        public void Pause()
        {
            scriptPaused = true;
            status.brush = Brushes.Orange;
            WriteEvent("Script Paused!");
        }

        /// <summary>
        /// Terminates the thread if the Script is not properly behaving.
        /// </summary>
        public void Terminate()
        {
            try
            {
                EndStop();
                scriptRunning = false;
                scriptPaused = false;
                scriptThread.Abort();
            }
            catch
            {
            }
        }

        private bool InitializeScript()
        {
            try
            {
                scriptAssembly = Assembly.LoadFrom(ScionSettings.StartupPath + @"\Scripts\" + scriptName + ".dll");
            }
            catch
            {
                return false;
            }
            foreach (Type scriptClass in scriptAssembly.GetTypes())
            {
                if (scriptClass.IsClass && scriptClass.IsSubclassOf(typeof(Superior)))
                {
                    ConstructorInfo[] constructors = scriptClass.GetConstructors();
                    foreach (ConstructorInfo constructor in constructors)
                    {
                        if (constructor.IsConstructor)
                        {
                            try
                            {
                                scriptObject = constructor.Invoke(new object[] { stub });
                                scriptInstance = scriptObject.GetType();
                            }
                            catch (Exception ex)
                            {
                                stub.botLog.WriteLine("Error running script/random:" + System.Environment.NewLine + ex.InnerException.ToString());
                                return false;
                            }
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        private MethodInfo FindExecution()
        {
            foreach (MethodInfo method in scriptInstance.GetMethods())
            {
                if (method.Name == "Execution")
                    return method;
            }
            return null;
        }

        private MethodInfo FindExecutionLoop()
        {
            foreach (MethodInfo method in scriptInstance.GetMethods())
            {
                if (method.Name == "ExecutionLoop")
                    return method;
            }
            return null;
        }

        public void FailProtect()
        {
            if (!scriptSkipCheckTabs)
                if (safeFail.CheckMissingTabs())
                    safeFail.SolveMissingTabs();
            if (safeFail.CheckConnectionLost())
                safeFail.SolveConnectionLost();
            if (safeFail.IsLoading())
                while (safeFail.IsLoading())
                    safeFail.Sleep(new SleepRandom(1000, 1100));
        }

        private void Loop()
        {
            bool result = InitializeScript();
            if (!result)
                goto End;
            MethodInfo scriptLoop = FindExecutionLoop();
            if (scriptLoop == null)
                goto End;
            stub.botRandom.InitializeRandoms();
            if (!stub.botAccount.AccountLogin())
                goto End;
            MethodInfo execution = FindExecution();
            if (execution != null)
            {
                object rType = execution.Invoke(scriptObject, null);
                if (rType is KillExecution)
                    goto End;
                else if (rType is SleepRandom)
                    Thread.Sleep(((SleepRandom)rType).random);
            }
            //Our execution loop. Randoms get excuted through each return loop.
            while (true)
            {
                if (scriptPaused)
                {
                    blockedInput = false;
                    stub.botBridge.SyncronizeBridge();
                    Thread.Sleep(1000);
                }
                else if (!scriptRunning)
                    goto End;
                else
                {
                    blockedInput = true;
                    try
                    {
                        if (!stub.botAccount.DisableAutoLogin)
                        {
                            bool rturn = stub.botRandom.ProcessAllRandoms();
                            if (!rturn)
                            {
                                if (safeFail.IsLoggedIn())
                                    safeFail.Interface.LogOut();
                                goto End;
                            }
                        }
                        object returnType = scriptLoop.Invoke(scriptObject, null);
                        FailProtect();
                        if (returnType is KillExecution)
                            break;
                        else if (returnType is Nothing)
                            continue;
                        else if (returnType is SleepRandom)
                            Thread.Sleep(((SleepRandom)returnType).random);
                        else if ((returnType == null) || (returnType is Exception))
                            break;
                    }
                    catch (Exception ex)
                    {
                        if (!(ex is ThreadAbortException))
                            stub.botLog.WriteLine("Error running script/random:" + System.Environment.NewLine + ex.InnerException.ToString());
                        break;
                    }
                }
            }
        End:
            Stop();
            EndStop();
        }
    }
}
